import cloneDeep from 'lodash/cloneDeep';
import findIndex from 'lodash/findIndex';
import keyBy from 'lodash/keyBy';
import uniq from 'lodash/uniq';

/**
 * @desc tool class
 */
class Tool {
    /**
     * @desc format origin data
     * @param {object} originData {nodes, links}
     * nodes { id: 'id' ... }
     * links { from: 'id1', to: 'id2' }
     * @return {object}
     *   nodes { ..., childs: [] }
     *   links { fromNode: node, toNode: node }
     *   nodesKV {id1: node1, id2: node2}
     *   linksKV {id1: link1, id2: link2}
     *   linksGroup {from-to: [], from-to: []}
     */
    toGridData(originData) {
        // console.time('toGridData');
        let { links, nodes } = cloneDeep(originData);
        let linksGroup = {};
        let nodesKV = null;
        let linksKV = null;
        try {
            nodesKV = keyBy(nodes, 'id');
            linksKV = keyBy(links, 'id');

            // set childs
            nodes.forEach(d => {
                d.childs = [];
            });

            for (let i = 0; i < links.length; i++) {
                let d = links[i];

                d['fromNode'] = nodesKV[d.from] || {};
                d['toNode'] = nodesKV[d.to] || {};

                // Exclude duplicate data
                if (
                    findIndex(d.fromNode['childs'], item => {
                        return item.id === d.toNode.id;
                    }) === -1
                ) {
                    d.fromNode['childs'].push(d.toNode);
                }
                if (
                    findIndex(d.toNode['childs'], item => {
                        return item.id === d.fromNode.id;
                    }) === -1
                ) {
                    d.toNode['childs'].push(d.fromNode);
                }

                // set keys id1-id2
                if (!linksGroup[d.from + '-' + d.to]) {
                    linksGroup[d.from + '-' + d.to] = [];
                }
                linksGroup[d.from + '-' + d.to].push(d);
            }
        } catch (e) {
            console.error('data error!');
        }
        // console.timeEnd('toGridData', { links, nodes, nodesKV, linksKV, linksGroup });
        return { links, nodes, nodesKV, linksKV, linksGroup };
    }

    /**
     * @desc get random id
     * @param {number} randomLength
     */
    getRandomID(randomLength = 8) {
        return Number(
            Math.random()
                    .toString()
                    .substr(3, randomLength || 8) + Date.now()
        ).toString(36);
    }

    /**
     * @desc get the correlation line by id
     */
    getLineById(id, gdata) {
        if (!gdata) {
            console.error('gdata 为 null');
            return false;
        }
        const { nodesKV, linksGroup } = gdata;
        // get the relevant links and redraw the lines
        let arr = [];
        nodesKV[id].childs.forEach(d => {
            let key1 = `${id}-${d.id}`;
            let key2 = `${d.id}-${id}`;

            if (linksGroup[key1]) {
                arr = arr.concat(linksGroup[key1]);
            }
            if (linksGroup[key2]) {
                arr = arr.concat(linksGroup[key2]);
            }
        });
        return arr;
    }

    /**
     * @desc drag group
     * @param {object} e
     * @param {zrender.Group} group  { position, data }
     * @param {array} scale [ 1 , 1]
     * @param {function} callback [ 1 , 1]
     */
    dragGroup(e, group, callback, scale) {
        const { clientX, clientY } = e.event;
        const [scaleX, scaleY] = scale || [1, 1];
        let [x0, y0] = group.position;
        const moveFun = em => {
            let x = (em.clientX - clientX) / scaleX;
            let y = (em.clientY - clientY) / scaleX;
            group.attr('position', [x0 + x, y0 + y]);

            // for update path
            if (group.data) {
                group.data.x = x0 + x;
                group.data.y = y0 + y;
            }
            callback && callback(group);
        };
        const upFun = () => {
            document.removeEventListener('mousemove', moveFun);
            document.removeEventListener('mouseup', upFun);
        };
        document.addEventListener('mousemove', moveFun);
        document.addEventListener('mouseup', upFun);
    }

    /**
     * @desc Judge whether the two points intersect
     */
    judgeCross(fromNode, toNode) {
        let { x, y, width, height } = fromNode;
        // The border of toNode
        let toDot = [
            [toNode.x, toNode.y],
            [toNode.x + toNode.width, toNode.y],
            [toNode.x + toNode.width, toNode.y + toNode.height],
            [toNode.x, toNode.y + toNode.height]
        ];
        let inRect = [
            { dirc: 'upLeft', mark: false },
            { dirc: 'upRight', mark: false },
            { dirc: 'bottomRight', mark: false },
            { dirc: 'bottomLeft', mark: false }
        ];
        toDot.forEach((d, i) => {
            if (d[0] >= x && d[0] <= x + width && d[1] >= y && d[1] <= y + height) {
                inRect[i].mark = true;
            }
        });
        let isCross = false;
        inRect.forEach(d => {
            if (d.mark) {
                isCross = true;
            }
        });
        return { isCross, inRect };
    }

    /**
     * @desc delete by ids
     * @param {string} id ids [id1, id2]
     * @return {nodes, links } {nodes: [id1, id2] links: [id1, id2]}
     */
    delById(id, gdata) {
        let data = {
            nodes: [id],
            links: []
        };
        let { nodesKV, linksGroup } = gdata;
        nodesKV[id].childs.forEach(d => {
            let lines1 = linksGroup[`${id}-${d.id}`];
            let lines2 = linksGroup[`${d.id}-${id}`];
            if (lines1) {
                data.links = [
                    ...data.links,
                    ...lines1.map(d => {
                        return d.id;
                    })
                ];
            }
            if (lines2) {
                data.links = [
                    ...data.links,
                    ...lines2.map(d => {
                        return d.id;
                    })
                ];
            }
        });
        data.links = uniq(data.links);
        return data;
    }
}

export default new Tool();
